if CLIENT then
	CreateClientConVar ("ictg", "", false, true)
	CreateClientConVar ("ictgl", "", false, true)
end

local function AngleBetween( a, b )
	return math.deg( math.acos( a:Dot( b ) ) )
end

local function LineOfSight (ent, pos1, pos2, filt)
	return util.TraceLine ({
		start = pos1,
		endpos = pos2,
		filter = filt,
		mask = MASK_SHOT
	}).Entity == ent
end

local function BoneLocation (ent, boneName)
	local bone = ent:LookupBone(boneName)
	if bone then
		local pos = ent:GetBonePosition (bone)
		if pos then return pos end
	end
end

GM.Lockon_LegitTargets = {
	npc_combine_s = {
		bones = {
			"ValveBiped.Bip01_Spine2",
			"ValveBiped.Bip01_Head1",
			"ValveBiped.Bip01_Pelvis",
			"ValveBiped.Bip01_L_Calf",
			"ValveBiped.Bip01_R_Calf",
			--"ValveBiped.Bip01_L_Forearm", --removed to improve headshot detection
			--"ValveBiped.Bip01_R_Forearm",
		}
	},
	npc_metropolice = {
		bones = {
		"ValveBiped.Bip01_Spine2",
		"ValveBiped.Bip01_Head1",
		"ValveBiped.Bip01_Pelvis",
		"ValveBiped.Bip01_L_Calf",
		"ValveBiped.Bip01_R_Calf",
		--"ValveBiped.Bip01_L_Forearm",
		--"ValveBiped.Bip01_R_Forearm",
		}
	},
}

if CLIENT then
	
	GM.DefaultMaxAng = 5
	
	function GM:Lockon_Think ()
		local ent = self:Lockon_GetTarget()
		if ent then
			if self:Lockon_TargetValid(ent) then
				local vNorm = LocalPlayer():GetAimVector()
				local sPos = LocalPlayer():GetShootPos()
				local reqScore = (LocalPlayer():GetActiveWeapon().LockonMaxAng or self.DefaultMaxAng) / (1 + (ent:GetPos() - sPos):Length() / 1000)
				local bones = self.Lockon_LegitTargets[ent:GetClass()].bones
				local gotReqScore = false
				for id,bone in pairs(bones) do
					if AngleBetween (vNorm, (BoneLocation (ent, bone) - sPos):GetNormal()) < reqScore then
						gotReqScore = true
						reqScoreBoneID = id
					end
				end
				if gotReqScore then
					if LineOfSight (ent, sPos, BoneLocation (ent, bones[reqScoreBoneID]), LocalPlayer()) then
						RunConsoleCommand ("ictgl", reqScoreBoneID)
						return
					end
				end
			end
		end
		
		if self:Lockon_GetTarget() then
			self:Lockon_SetTarget (false)
		end
		self:Lockon_SelectNewTarget()
	end
	
	function GM:Lockon_GetTarget ()
		return self.Lockon_Target
	end
	
	function GM:Lockon_SetTarget (ent)
		self.Lockon_Target = ent
		if ent then
			printd ("New lock: ", tostring(ent))
			RunConsoleCommand ("ictg", ent:EntIndex())
		else
			printd ("Lock released")
			RunConsoleCommand ("ictg", -1)
		end
	end
	
	function GM:Lockon_TargetValid (ent)
		return type(self.Lockon_LegitTargets[ent:GetClass()]) == "table"
	end
	
	function GM:Lockon_SelectNewTarget ()
		local vNorm = LocalPlayer():GetAimVector()
		local sPos = LocalPlayer():GetShootPos()
		local bestTarg = nil
		local bestScore = LocalPlayer():GetActiveWeapon().LockonMaxAng or self.DefaultMaxAng
		
		for _,ent in pairs (ents.GetAll()) do
			if self:Lockon_TargetValid (ent) then
				local bones = self.Lockon_LegitTargets[ent:GetClass()].bones
				if AngleBetween (vNorm, (BoneLocation (ent, bones[1]) - sPos):GetNormal()) < 25 then
					local boneAngs = {}
					local score = 200
					local scoreBoneID = 1
					for id,bone in pairs(bones) do
						local boneScore = AngleBetween (vNorm, (BoneLocation (ent, bone) - sPos):GetNormal())
						if boneScore < score then
							score = boneScore
							scoreBoneID = id
						end
					end
					--printd (score)
					score = score * (1 + (ent:GetPos() - sPos):Length() / 1000)
					--printd (" ", score, bestScore)
					if score < bestScore then
						if LineOfSight (ent, sPos, BoneLocation (ent, bones[scoreBoneID]), LocalPlayer()) then
							bestTarg = ent
							bestTargBone = scoreBoneID
							bestScore = score
						end
					end
				end
			end
		end
		
		if bestTarg then
			self:Lockon_SetTarget (bestTarg)
			RunConsoleCommand ("ictgl", bestTargBone)
		end
	end

	GM:AddHook ("Think", "Lockon_Think")
	
else
	
	--[[function GM:Lockon_ReceiveTarget (ply,cmd,args)
		printd (ply, args[1])
		ply.Lockon_Target = nil
		local targ = Entity (args[1])
		if (!IsValid(targ)) then return end
		
		ply.Lockon_Target = targ
	end
	
	GM:AddConCommand ("ictg", "Lockon_ReceiveTarget")]]
	--concommand.Add ("t", function () GAMEMODE:Lockon_ReceiveTarget() end)
	
end

local ply = FindMetaTable ("Player")
if !ply then return end

function ply:GetLockonAimVector ()
	local ent
	if SERVER then
		local id = tonumber(self:GetInfo ("ictg"))
		if id then
			ent = Entity(id)
		end
	end
	if CLIENT then
		ent = GAMEMODE:Lockon_GetTarget()
	end
	if !IsValid(ent) then return end
	
	if not GAMEMODE.Lockon_LegitTargets[ent:GetClass()] then return end
	local bones = GAMEMODE.Lockon_LegitTargets[ent:GetClass()].bones
	local boneid = tonumber(self:GetInfo ("ictgl"))
	return (BoneLocation (ent, bones[boneid or 1]) - self:GetShootPos()):GetNormal()
end

